﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.Windows.Interactivity;
using System.Threading;
using Odyssey.Effects;
using Odyssey.Utils;

namespace Odyssey.Behaviors
{
    public abstract class PageEffectBehavior : Behavior<PhoneApplicationPage>
    {

        /// <summary>
        /// Gets or sets whether the effect is enabled.
        /// </summary>
        public bool IsEnabled { get; set; }

        /// <summary>
        /// Gets or sets whether to wait until the effect is completed until navigation back.
        /// </summary>
        public bool WaitUntilCompleted { get; set; }

        /// <summary>
        /// Gets or sets the delay that occurs when navigating back until the previous page is layouted.
        /// If WaitUntilCompleted is set to true, this value specifies the duration to substact from the duratio of the effect which is used to wait until navigating back.
        /// </summary>
        public TimeSpan BackDelay { get; set; }


        public static string GetPageEffectKey(DependencyObject obj)
        {
            return (string)obj.GetValue(PageEffectKeyProperty);
        }

        public static void SetPageEffectKey(DependencyObject obj, string value)
        {
            obj.SetValue(PageEffectKeyProperty, value);
        }

        public static readonly DependencyProperty PageEffectKeyProperty =
            DependencyProperty.RegisterAttached("PageEffectKey", typeof(string), typeof(PageEffectBehavior), new PropertyMetadata(null, OnPageEffectKeyChanged));

        private static void OnPageEffectKeyChanged(DependencyObject o, DependencyPropertyChangedEventArgs args)
        {
            PhoneApplicationPage page = o as PhoneApplicationPage;
            if (page == null) throw new ArgumentException("PageEffectKey must be set on PhoneApplicationPage only.");
            string key = args.NewValue as string;
            if (!string.IsNullOrEmpty(key))
            {
                PageEffectBehavior behavior = page.Resources[key] as PageEffectBehavior;
                if (behavior != null)
                {
                    System.Windows.Interactivity.Interaction.GetBehaviors(page).Add(behavior);
                }
            }
        }

    }

    /// <summary>
    /// abstract base class for page transition effects which apply when navigating from and to the page.
    /// </summary>
    public abstract class PageEffectBehavior<E> : PageEffectBehavior where E : PageEffect
    {
        /// <summary>
        /// Gets the duration for the effect.
        /// </summary>
        public TimeSpan Duration
        {
            get { return Effect.Duration; }
            set { Effect.Duration = value; }
        }

        protected E Effect { get; private set; }
        private bool allowBack;


        public PageEffectBehavior()
            : base()
        {
            Effect = System.Activator.CreateInstance<E>();
            IsEnabled = true;
            WaitUntilCompleted = true;
        }


        protected override void OnAttached()
        {
            Effect.Page = AssociatedObject;
            Effect.IsInitialized = false;

            AssociatedObject.LayoutUpdated += new EventHandler(OnLayoutUpdated);
            AssociatedObject.Unloaded += new RoutedEventHandler(OnUnloaded);
            AssociatedObject.Loaded += new RoutedEventHandler(OnLoaded);
            base.OnAttached();
        }

        void OnLoaded(object sender, RoutedEventArgs e)
        {
            allowBack = false;
            AssociatedObject.NavigationService.Navigating += new System.Windows.Navigation.NavigatingCancelEventHandler(OnNavigating);
        }

        void OnUnloaded(object sender, RoutedEventArgs e)
        {
            AssociatedObject.NavigationService.Navigating -= OnNavigating;
        }

        protected override void OnDetaching()
        {
            AssociatedObject.LayoutUpdated -= OnLayoutUpdated;
            var ns = AssociatedObject.NavigationService;
            if (ns != null) ns.Navigating -= OnNavigating;
            base.OnDetaching();
        }

        void OnNavigating(object sender, System.Windows.Navigation.NavigatingCancelEventArgs e)
        {
            if (!allowBack && IsEnabled)
            {
                if (!e.Cancel && e.NavigationMode == System.Windows.Navigation.NavigationMode.Back)
                {
                    allowBack = WaitUntilCompleted;

                    bool isCurrent = (Application.Current.RootVisual as PhoneApplicationFrame).Content == AssociatedObject;
                    if (isCurrent)
                    {
                        AssociatedObject.NavigationService.Navigating -= OnNavigating;

                        Effect.IsActive = true;

                        int miliseconds = (int)(Effect.DurationInSeconds * 1000.0 - BackDelay.TotalMilliseconds);
                        if (allowBack && miliseconds > 0)
                        {
                            Dispatcher.BeginInvoke(() => Thread.Sleep(miliseconds));
                        }
                    }
                }
            }
        }

        void OnLayoutUpdated(object sender, EventArgs e)
        {
            if (IsEnabled)
            {
                double duration = Duration.TotalSeconds;

                AssociatedObject.LayoutUpdated -= OnLayoutUpdated;

                Effect.IsActive = false;
            }
        }


        public event EventHandler Activated
        {
            add { Effect.Activated += value; }
            remove { Effect.Activated -= value; }
        }

        public event EventHandler Deactivated
        {
            add { Effect.Deactivated += value; }
            remove { Effect.Deactivated -= value; }
        }


    }
}
